Selector 选择器

Note

选择器的使命就是完成 IO 多路复用,通过选择器可以同时监控多个通道的 IO 事件。选择器与通道的关系就是监控和被监控的关系

基于之前对 IO 多路复用 的理解我们可以知道,一条线程处理一个选择器,而一个选择器可以监控很多的通道。所以这意味着通过选择器一条线程可以处理成百上千的通道,大量的减少了线程之间上下文切换的开销。

通道和选择器之间通过 register (注册) 的方式完成,调用通道的 Channel.register(Selector sel, int ops) 方法,可以将通道实例注册到一个选择器中。第一个参数指定选择器实例,第二个参数指定 IO 事件类型。

  • 📌 可供选择器监控的通道IO事件类型,包括以下四种:(1)可读:SelectionKey.OP_READ(2)可写:SelectionKey.OP_WRITE(3)连接:SelectionKey.OP_CONNECT(4)接收:SelectionKey.OP_ACCEPT

事件类型的定义在 SelectionKey 类中。如果选择器要监控通道的多种时间,可以用 " 按位或 " 运算符来实现。

        //监控通道的多种事件,用“按位或”运算符来实现
        int key = SelectionKey.OP_READ | SelectionKey.OP_WRITE ;
注意

这里的 IO 事件指的不是对通道的 IO 操作,而是通道的某个 IO 操作的一种就绪状态,比如 SocketChannel 通道, 完成握手连接则处于 " 连接就绪状态 " (OP_CONNECT)

判断一个通道能否被选择

一个通道是否能被选择,首先要看是否继承 SelectableChannel 类,如果继承了就可以被选择,否则不能。像 FileChannel 就没有继承 SelectableChannel,所以不可以选择的通道。

SelectionKey 选择键

一旦在通道中发生了某些 IO 事件(就绪状态达成),并且是在选择器中注册过的 IO 事件,就会被选择器选中,并放入 SelectionKey 选择键的集合中。

Note

SelectionKey 选择键就是那些被选择器选中的 IO 事件,那些没有被注册过的通道即使发生了 IO 事件也不会被选择器选中放入选择键集合中

通过选择键不仅仅可获得通道的 IO 事件类型,也可以获得 IO 事件所在的通道,还有选出选择键的选择器实例